#ifndef UDP_FEC_PACKET_H_
#define UDP_FEC_PACKET_H_

#ifdef WIN32
#include <Winsock2.h>
#else
#include <arpa/inet.h>
#endif

#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <list>
#include <algorithm>
#include <memory>

#define UDP_EFC_PACKET_BODY_MAX_SIZE 1300

#pragma pack(push,1)
struct UdpFecHead
{
    uint64_t block_id;
    uint16_t block_size;
    uint16_t parity_num;
    uint16_t segment_id;
};

struct UdpFecPacketMsg
{
    uint8_t               msg_id;
    uint16_t              msg_data_length;
    uint8_t               msg_data[UDP_EFC_PACKET_BODY_MAX_SIZE];

    uint8_t               get_msg_id() { return msg_id; }
    uint16_t              get_msg_data_length() 
    { 
        int len = ntohs(msg_data_length);
        assert(len <= UDP_EFC_PACKET_BODY_MAX_SIZE);
        return len;  
    }
    uint8_t*              get_msg_data() { return msg_data; };
};

struct FecPacket
{
public:
    UdpFecHead head;
    union  Body
    {
        UdpFecPacketMsg msg;
        uint8_t                   fec_payload[sizeof(UdpFecPacketMsg)];
    }body;

public:
    FecPacket()
    {
        memset(&head, 0, sizeof(head));
        memset(&body, 0, sizeof(body));
    }

    uint16_t  get_packet_length()
    {
        //norm data
        if (segment_id()<block_size())
            //sizeof(uint8_t) + sizeof(uint16_t) + get_msg_data_length() = fecudppacket
            return sizeof(head) + sizeof(uint8_t) + sizeof(uint16_t) + get_msg_data_length();
        //fec data
        return sizeof(head) + sizeof(UdpFecPacketMsg);
    }
    const char* get_packet_payload() { return (const char*)&head; }

    uint8_t get_msg_id() { return body.msg.get_msg_id(); }
    void set_msg_id(int id) { body.msg.msg_id = id; }

    void set_fecpayload(char *data, int len) 
    { 
        assert(len <= sizeof(UdpFecPacketMsg));
        memcpy(body.fec_payload, data, len); 
    }
    uint8_t*  get_fecpayload() { return body.fec_payload; }

    void set_msg_data(const char *data, int len)
    {
        assert(len <= UDP_EFC_PACKET_BODY_MAX_SIZE);
        body.msg.msg_data_length = htons(len);
        memcpy(body.msg.msg_data, data, len);
    }
    /*
    void set_msg_data_length(int len)
    {
        assert(len <= UDP_EFC_PACKET_BODY_MAX_SIZE);
        body.msg.msg_data_length = htons(len);
    }*/
    uint16_t  get_msg_data_length() { return body.msg.get_msg_data_length(); }
    uint8_t*  get_msg_data() { return body.msg.get_msg_data(); }
    uint16_t  get_msg_data(uint8_t* buf, int len) 
    { 
        int cplen = std::min<uint16_t>(len, get_msg_data_length());
        memcpy(buf, body.msg.msg_data, cplen);
        return cplen;
    }

    uint16_t  segment_id() { return ntohs(head.segment_id); }
    uint16_t  block_size() { return ntohs(head.block_size); }
    uint16_t  parity_num() { return ntohs(head.parity_num); }
    uint64_t block_id() { return ntohl(head.block_id); }

    void set_segment_id(int id) { head.segment_id = htons(id); }
    void set_block_size(int size) { head.block_size = htons(size); }
    void set_parity_num(int num) { head.parity_num = htons(num); }
    void set_block_id(int id) { head.block_id = htonl(id); }
};
#pragma pack(pop)

typedef std::shared_ptr<FecPacket> SpFecPacket;
typedef std::list<SpFecPacket> FecPacketList;
typedef std::list<std::string> MessageList;

#endif//MESSAGE_ENCDOE_BUFFER_H_